package tech.heptagram.staff.cas.service.staff.impl;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import tech.heptagram.staff.cas.common.ArgumentException;
import tech.heptagram.staff.cas.common.AssertUtils;
import tech.heptagram.staff.cas.domain.MenuType;
import tech.heptagram.staff.cas.domain.info.MenuInfo;
import tech.heptagram.staff.cas.domain.info.MenuTreeVo;
import tech.heptagram.staff.cas.domain.po.Menu;
import tech.heptagram.staff.cas.domain.po.Role;
import tech.heptagram.staff.cas.domain.po.RoleMenu;
import tech.heptagram.staff.cas.mapper.CasMenuMapper;
import tech.heptagram.staff.cas.mapper.CasRoleMenuMapper;
import tech.heptagram.staff.cas.service.staff.CasMenuService;
import tech.heptagram.staff.cas.service.staff.CasRoleService;

/**
 * 菜单相关操作与权限相关
 * Created by yang on 2017/7/6.
 */
@Service
public class CasMenuServiceImpl implements CasMenuService {
    private static final int LABEL_PAGE = 0;

    private static final int NOT_MENU_PAGE = 3;

    private static final int HAS_PATH = 1;

    /**
     * admin 角色默认ID
     */
    private static final int ADMIN_ROLE_ID = 1;

    @Resource
    private CasMenuMapper casMenuMapper;

    @Resource
    private CasRoleMenuMapper casRoleMenuMapper;

    @Resource
    private CasRoleService casRoleService;

    /**
     * 保存菜单
     *
     * @param menu menuVo
     * @return menu Id
     */
    @Override
    @Transactional
    public Integer save(Menu menu) {
        assertMenu(menu);
        AssertUtils.notNull(menu.getCreatedBy(), "创建人不能为空");
        AssertUtils.notNull(menu.getParentId(), "父菜单编号不能为空");


        //生成新的父ID字符串，父ID字符串样式如 0;1;3;6;
        Menu parentMenu = findById(menu.getParentId());
        String parentIdStrand = parentMenu.getParentIdStrand() + menu.getParentId() + ";";

        // 插入menu表
        menu.setParentIdStrand(parentIdStrand);
        menu.setModifiedBy(menu.getCreatedBy());
        casMenuMapper.insert(menu);

        // 更新app字段
        try{
            if(StringUtils.isNotEmpty(menu.getApp())) {
                casMenuMapper.updateAppField(menu.getId(), menu.getApp());
            }
        }catch (Exception ex) {

        }

        // 默认新菜单加入admin角色权限
        RoleMenu roleMenu = new RoleMenu();
        roleMenu.setModifiedBy(menu.getCreatedBy());
        roleMenu.setCreatedBy(menu.getCreatedBy());
        roleMenu.setRoleId(ADMIN_ROLE_ID);
        roleMenu.setMenuId(menu.getId());
        casRoleMenuMapper.insert(roleMenu);

        return menu.getId();
    }

    @Override
    @Transactional
    public void delete(Integer id, String deletedBy) {
        AssertUtils.notNull(deletedBy, "删除人不能为空");
        AssertUtils.notNull(id, "菜单编号不能为空");

        // 如果有子菜单,不允许删除
        List<Menu> subMenus = casMenuMapper.selectByParentId(id);
        if (!CollectionUtils.isEmpty(subMenus)) {
            throw new ArgumentException("菜单下面有还有子菜单，请先清空子菜单");
        }

        // 删除菜单
        casMenuMapper.deletedById(id, deletedBy);

        // 删除角色菜单关系
        casRoleMenuMapper.deleteByMenuId(id, deletedBy);
    }

    @Override
    public void update(Menu menu) {
        assertMenu(menu);
        AssertUtils.notNull(menu.getModifiedBy(), "修改人不能为空");

        casMenuMapper.update(menu);

        // 更新app字段
        try{
            if(StringUtils.isNotEmpty(menu.getApp())) {
                casMenuMapper.updateAppField(menu.getId(), menu.getApp());
            }
        }catch (Exception ex) {

        }
    }


    @Override
    public MenuTreeVo findTree(Integer parentId) {
        if (Objects.isNull(parentId)) {
            return new MenuTreeVo();
        }
        MenuTreeVo menuTreeVo = casMenuMapper.selectMenuTree(parentId);
        if (menuTreeVo == null) {
            return new MenuTreeVo();
        }
        return menuTreeVo;
    }

    /**
     * 获取指定角色的菜单ID列表
     *
     * @param roleId
     * @return
     */
    @Override
    public List<Integer> findIdsByRoleId(Integer roleId) {
        List<Menu> menus = casMenuMapper.selectAllMenuByRoleId(roleId);
        return menus.stream().map(Menu::getId).collect(Collectors.toList());
    }

    @Override
    public List<Menu> findByParentId(Integer parentId) {
        if (Objects.isNull(parentId)) {
            return new ArrayList<>();
        }

        try {
            List<Menu> menus = casMenuMapper.selectByParentIdWithAppField(parentId);
            return menus;
        } catch (Exception ex) {
            List<Menu> lstMenus = casMenuMapper.selectByParentId(parentId);
            return lstMenus;
        }
    }

    @Override
    public List<Menu> findByPath(String path) {
        if (StringUtils.isEmpty(path)) {
            return new ArrayList<>();
        }

        List<Menu> menus = casMenuMapper.selectByPath(path);
        return menus;
    }

    @Override
    public List<Menu> findOperateByStaffCodeAndMenuId(String staffCode, Integer menuId) {
        if(StringUtils.isEmpty(staffCode) || Objects.isNull(menuId)) {
            return new ArrayList<>();
        }

        List<Menu> lstMenus = findOperateByStaffCode(staffCode);

        List<Menu> operates = lstMenus.stream()
            .filter(m -> m.getParentId().intValue() == menuId)
            .collect(Collectors.toList());
        return operates;
    }

    @Override
    public List<Menu> findOperateByStaffCode(String staffCode) {
        if(StringUtils.isEmpty(staffCode)) {
            return new ArrayList<>();
        }

        // 获取员工的所有角色Ids, 如果没有设置任何角色, 就没有操作权限
        List<Role> roles = casRoleService.findRolesByStaffCode(staffCode);
        List<Integer> lstRoleIds = roles.stream().map(Role::getId).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(lstRoleIds)){
            return new ArrayList<>();
        }

        List<Menu> menus = casMenuMapper.selectOperateByRoleIds(lstRoleIds);
        return menus;
    }

    @Override
    public Menu findById(Integer id) {
        if (Objects.isNull(id)) {
            return null;
        }

        Menu menu = casMenuMapper.selectById(id);
        return menu;
    }

    /**
     * check the save menu Vo
     *
     * @param vo menuVo
     */
    private void assertMenu(Menu vo) {
        AssertUtils.notNull(vo, "菜单不能为Null");
        AssertUtils.hasText(vo.getName(), "菜单名称不能为空");
        AssertUtils.notNull(vo.getType(), "菜单类型不能为空");

        // 标签和分组不需要设置url
        if (vo.getType() == MenuType.LABEL.getValue() || vo.getType() == MenuType.MENU_GROUP.getValue()) {
            vo.setPath("");
        }

        // 除了标签和分组之外的page都需要设置url
        if (vo.getType() != MenuType.LABEL.getValue()
            && vo.getType() != MenuType.MENU_GROUP.getValue()
            && vo.getType() != MenuType.OPERATE.getValue()) {
            if (StringUtils.isEmpty(vo.getPath())) {
                throw new ArgumentException("菜单路径不能为空");
            }
            List<Menu> lstPathMenus = findByPath(vo.getPath());
            if (!CollectionUtils.isEmpty(lstPathMenus)) {
                for (Menu pathMenu : lstPathMenus) {
                    if (Objects.isNull(vo.getId()) || vo.getId().intValue() != pathMenu.getId()) {
                        throw new ArgumentException("已经拥有同样的路径");
                    }
                }
            }
        }
    }


    @Override
    public MenuInfo findMenusByStaffCode(String staffCode) {
        return findMenusByStaffCode(staffCode, null);
    }

    @Override
    public MenuInfo findMenusByStaffCode(String staffCode, String menuApp) {
        if(StringUtils.isEmpty(staffCode)){
            return null;
        }

        // 获取员工的所有角色Ids, 如果没有设置任何角色, 就看不到任何菜单
        List<Role> roles = casRoleService.findRolesByStaffCode(staffCode);
        List<Integer> lstRoleIds = roles.stream().map(Role::getId).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(lstRoleIds)){
            return null;
        }

        // 获取角色的所有菜单
        List<Menu> lstStaffMenus = casMenuMapper.selectByRoleIdsAndApp(lstRoleIds, menuApp);

        // 处理一下员工菜单
        lstStaffMenus = resolveStaffMenus(lstStaffMenus);

        // 根菜单parent_id = 0
        final int ROOT_MENU_PARENT_ID = 0;
        // 获取根菜单
        List<Menu> rootMenus = casMenuMapper.selectByParentId(ROOT_MENU_PARENT_ID);
        if(CollectionUtils.isEmpty(rootMenus) || rootMenus.size() != 1){
            throw new ArgumentException("该系统没有根菜单, 或根菜单数量过多");
        }
        Menu rootMenu = rootMenus.get(0);
        lstStaffMenus.add(rootMenu);

        // 递归获取所有的菜单
        MenuInfo menuInfo = getMenuInfo(rootMenu.getId(), lstStaffMenus);
        return menuInfo;
    }



    /**
     * 处理一下员工拥有的菜单权限
     *
     * 存在一种情况, 员工拥有某个子菜单权限,但没有拥有其父菜单权限(因为父菜单下有多个子菜单,商务只拥有部分子菜单权限)
     * 这种情况下, 需要把父菜单添加到权限列表中, 否则员工在系统中会查不到整个菜单的菜单
     *
     * @param staffMenus
     * @return
     */
    private List<Menu> resolveStaffMenus(List<Menu> staffMenus) {
        List<Menu> resolveResult = new LinkedList<>();
        resolveResult.addAll(staffMenus);

        // 根菜单parent_id = 0
        final int ROOT_MENU_PARENT_ID = 0;

        for (Menu staffMenu : staffMenus) {
            String[] parentIds = StringUtils.split(staffMenu.getParentIdStrand(), ";");
            if(ArrayUtils.isNotEmpty(parentIds)){
                for (String parentId : parentIds) {
                    if(StringUtils.isNotEmpty(parentId) && Integer.valueOf(parentId) != ROOT_MENU_PARENT_ID){
                        Integer intParentId = Integer.valueOf(parentId);
                        if(!menuExists(resolveResult, intParentId)){
                            Menu parentMenu = casMenuMapper.selectById(intParentId);
                            resolveResult.add(parentMenu);
                        }
                    }
                }
            }
        }

        return resolveResult;
    }

    private boolean menuExists(List<Menu> menus, Integer existsMenuId) {
        Optional<Menu> any = menus.stream()
            .filter(m -> m.getId() == existsMenuId.intValue())
            .findAny();
        return any.isPresent();
    }

    private MenuInfo getMenuInfo(Integer id, List<Menu> allMenus){

        Menu currentMenu = allMenus.stream().filter(m -> m.getId().intValue() == id).findFirst().orElse(null);
        if(Objects.isNull(currentMenu)){
            return null;
        }

        MenuInfo result = new MenuInfo();
        BeanUtils.copyProperties(currentMenu, result);
        result.setSubMenus(new ArrayList<>());

        // 获取当前菜单的子菜单
        List<Menu> lstSubMenus = allMenus.stream()
            .filter(m -> m.getParentId().equals(id)) // 获取子菜单
            .sorted(Comparator.comparingInt(m -> m.getViewOrder().intValue()))
            .collect(Collectors.toList());

        // 如果子菜单存在
        if(!CollectionUtils.isEmpty(lstSubMenus)){
            for (Menu subMenu : lstSubMenus) {
                MenuInfo subMenuInfo = getMenuInfo(subMenu.getId().intValue(), allMenus);
                if(Objects.nonNull(subMenuInfo)){
                    result.getSubMenus().add(subMenuInfo);
                }
            }
        }

        return result;
    }
}
